import { useModel } from "@umijs/max"
import { useEffect, useRef } from "react"
import { ModalType } from "../models/modalModel"

export default function websocketModel() {
  const wsIsOpenRef = useRef(false)
  const websocketRef = useRef(null)
  const pyCallbacksRef = useRef<Record<number, Function>>({})
  const callNumberRef = useRef<number>(0)
  const lastMessageTimeRef = useRef<number>(Date.now())
  const reconnectTimerRef = useRef<NodeJS.Timeout | null>(null)
  const messageCheckTimerRef = useRef<NodeJS.Timeout | null>(null)
  const hasShownOfflineModalRef = useRef<boolean>(false)
  const { showModal } = useModel("modalModel")
  const { updateDeviceState } = useModel("deviceModel")
  const { updateProduceData, isOperateMode } = useModel("produceModel")
  const { updateToolData } = useModel("toolModel")
  const { updateEapData } = useModel("eapModel")

  // 显示掉线提示
  const showOfflineModal = () => {
    if (!hasShownOfflineModalRef.current) {
      hasShownOfflineModalRef.current = true

      // 使用自定义的showModal方法，显示掉线弹窗
      showModal({
        type: ModalType.Offline,
        title: "连接断开",
        content: "程序已掉线，请重新开启程序。",
        visible: true
      })
    }
  }

  // 检查消息接收超时
  const checkMessageTimeout = () => {
    const now = Date.now()
    const timeSinceLastMessage = now - lastMessageTimeRef.current

    if (timeSinceLastMessage > 30000 && wsIsOpenRef.current) {
      console.log("30秒内未收到消息，可能已掉线")
      showOfflineModal()
    }
  }

  // 启动消息检查定时器
  const startMessageCheckTimer = () => {
    if (messageCheckTimerRef.current) {
      clearInterval(messageCheckTimerRef.current)
    }
    messageCheckTimerRef.current = setInterval(checkMessageTimeout, 2000)
  }

  // 停止消息检查定时器
  const stopMessageCheckTimer = () => {
    if (messageCheckTimerRef.current) {
      clearInterval(messageCheckTimerRef.current)
      messageCheckTimerRef.current = null
    }
  }

  const createWebSocket = () => {
    if (!wsIsOpenRef.current || websocketRef.current === null) {
      // 创建websocket连接,如果websocket没有打开或者websocket===null的时候
      const page = window.location.pathname.substring(1)
      const websocketAddr = `ws://localhost:9099/eel?page=${page}`
      const ws = new WebSocket(websocketAddr)

      // websokcet 打开事件
      ws.onopen = () => {
        console.log("WebSocket onopen")
        wsIsOpenRef.current = true
        websocketRef.current = ws
        lastMessageTimeRef.current = Date.now()

        // 连接成功后，启动消息检查定时器
        startMessageCheckTimer()

        // 连接成功，清除重连定时器
        if (reconnectTimerRef.current) {
          clearTimeout(reconnectTimerRef.current)
          reconnectTimerRef.current = null
        }

        // 重置掉线提示状态
        hasShownOfflineModalRef.current = false
      }

      // websocket 关闭事件
      ws.onclose = () => {
        console.log("WebSocket onclose")
        wsIsOpenRef.current = false
        websocketRef.current = null

        // 停止消息检查定时器
        stopMessageCheckTimer()

        // 设置10秒后检查是否重连成功
        reconnectTimerRef.current = setTimeout(() => {
          if (!wsIsOpenRef.current) {
            console.log("10秒内未重连成功")
            showOfflineModal()
          }
        }, 6000)
      }

      // websocket 监听消息事件
      ws.onmessage = (e) => {
        const message = JSON.parse(e.data)
        lastMessageTimeRef.current = Date.now() // 更新最后收到消息的时间
        dealPythonMessage(message)
      }
    }
  }

  // 组件卸载时清理定时器
  useEffect(() => {
    return () => {
      if (reconnectTimerRef.current) {
        clearTimeout(reconnectTimerRef.current)
      }
      if (messageCheckTimerRef.current) {
        clearInterval(messageCheckTimerRef.current)
      }
    }
  }, [])

  const objToJson = (obj: any) => {
    return JSON.stringify(obj, (k, v) => (v == undefined ? null : v))
  }

  const callPy = async (
    objName: string,
    funcName: string,
    args: any[]
  ): Promise<ResponseData> => {
    callNumberRef.current = callNumberRef.current + 1
    console.log(`in callPy: ${objName},${funcName}`, args)
    const callId = callNumberRef.current
    const callObject = {
      call: callId,
      objName: objName,
      funcName: funcName,
      args
    }
    if (websocketRef.current && wsIsOpenRef.current) {
      websocketRef.current.send(objToJson(callObject))
    }
    return new Promise(function (resolve) {
      pyCallbacksRef.current[callObject.call] = resolve
    })
  }

  const dealPythonMessage = (message: any) => {
    if (message.hasOwnProperty("return")) {
      const callId = message.return as number
      if (message.return in pyCallbacksRef.current) {
        console.log("recv message(return)", message.value)
        pyCallbacksRef.current[message.return](message.value)
        delete pyCallbacksRef.current[callId]
      }
    } else if (message.hasOwnProperty("ShowModal")) {
      console.log("recv message(ShowModal): ", message.ShowModal)
      showModal(message.ShowModal)
    } else if (message.hasOwnProperty("UpdateDeviceState")) {
      // console.log("recv message(UpdateDeviceState): ", message.UpdateDeviceState)
      updateDeviceState(message.UpdateDeviceState)
    } else if (message.hasOwnProperty("UpdateProduceData")) {
      console.log("recv message(UpdateProduceData): ", message.UpdateProduceData)
      if (!isOperateMode) {
        updateProduceData(message.UpdateProduceData)
      }
    } else if (message.hasOwnProperty("UpdateToolData")) {
      console.log("recv message(UpdateToolData): ", message.UpdateToolData)
      updateToolData(message.UpdateToolData)
    } else if (message.hasOwnProperty("UpdateEapData")) {
      console.log("recv message(UpdateEapData): ", message.UpdateEapData)
      updateEapData(message.UpdateEapData)
    }
  }

  return {
    createWebSocket,
    callPy,
    wsIsOpenRef,
    lastMessageTimeRef
  }
}
